//package com.piece.core.elasticsearch.support;
//
//import com.fasterxml.jackson.databind.node.ObjectNode;
//import com.piece.core.elasticsearch.annotations.Document;
//import com.piece.core.framework.enums.sql.SqlOperator;
//import com.piece.core.framework.enums.sql.SqlRelation;
//import com.piece.core.framework.support.bootstrap.DataTable;
//import com.piece.core.framework.support.bootstrap.Order;
//import com.piece.core.framework.support.bootstrap.QueryTable;
//import com.piece.core.framework.support.convert.Convert;
//import com.piece.core.framework.util.basic.SpringUtil;
//import com.piece.core.framework.util.collection.MapUtil;
//import com.piece.core.framework.util.object.BeanUtil;
//import com.piece.core.framework.util.object.ObjectUtil;
//import com.piece.core.framework.util.string.JsonUtil;
//import com.piece.core.framework.util.string.StringUtil;
//import lombok.Setter;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
//import org.elasticsearch.action.bulk.*;
//import org.elasticsearch.action.delete.DeleteRequest;
//import org.elasticsearch.action.index.IndexRequest;
//import org.elasticsearch.action.search.SearchRequest;
//import org.elasticsearch.action.search.SearchResponse;
//import org.elasticsearch.action.update.UpdateRequest;
//import org.elasticsearch.client.HttpAsyncResponseConsumerFactory;
//import org.elasticsearch.client.RequestOptions;
//import org.elasticsearch.client.RestHighLevelClient;
//import org.elasticsearch.client.indices.GetIndexRequest;
//import org.elasticsearch.client.indices.PutMappingRequest;
//import org.elasticsearch.common.text.Text;
//import org.elasticsearch.index.query.*;
//import org.elasticsearch.search.SearchHits;
//import org.elasticsearch.search.builder.SearchSourceBuilder;
//import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
//import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
//import org.elasticsearch.search.sort.SortOrder;
//import org.elasticsearch.xcontent.XContentBuilder;
//import org.elasticsearch.xcontent.XContentFactory;
//import org.elasticsearch.xcontent.XContentType;
//import org.springframework.beans.BeansException;
//import java.io.IOException;
//import java.lang.reflect.Field;
//import java.util.*;
//import java.util.concurrent.locks.ReentrantLock;
//
//@Slf4j
//@Setter
//public class SearchBuilder {
//
//    private static final String HIGHLIGHTER_PRE_TAGS = "<mark style=\"color:black;background-color:yellow;\">";
//    private static final String HIGHLIGHTER_POST_TAGS = "</mark>";
//    private static final ReentrantLock lock = new ReentrantLock();
//
//    private ESClientPool clientPool = null;
//    private RequestOptions options = null;
//    private static SearchBuilder instance;
//
//    public static SearchBuilder getInstance() {
//        if (null == instance) {
//            lock.lock();
//            if (null == instance) {
//                instance = new SearchBuilder();
//                try {
//                    instance.setClientPool(SpringUtil.getBean(ESClientPool.class));
//                    RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
//                    builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(30 * 1024 * 1024));
//                    instance.setOptions(builder.build());
//                } catch (BeansException e) {
//                }
//            }
//            lock.unlock();
//        }
//        return instance;
//    }
//
//    private IndexRequest indexRequest(String index, String id, Object object) {
//        return new IndexRequest(index).id(id).source(JsonUtil.toJson(convertToMap(object)), XContentType.JSON);
//    }
//
//    private UpdateRequest updateRequest(String index, String id, Object object) {
//        return new UpdateRequest(index, id).doc(convertToMap(object), XContentType.JSON);
//    }
//
//    private DeleteIndexRequest deleteIndexRequest(String index) {
//        return new DeleteIndexRequest(index);
//    }
//
//    private DeleteRequest deleteRequest(String index, String id) {
//        return new DeleteRequest(index, id);
//    }
//
//    private BulkRequest bulkRequest() {
//        return new BulkRequest().timeout("10s");
//    }
//
//    private boolean checkIndex(RestHighLevelClient client, String index) {
//        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
//        try {
//            return client.indices().exists(getIndexRequest, options);
//        } catch (IOException e) {
//            return false;
//        }
//    }
//
//    /**
//     * 插入索引数据
//     */
//    public void insert(Object document) throws Exception {
//        if (null != clientPool) {
//            RestHighLevelClient client = clientPool.getClient();
//            String index = convertIndex(document.getClass());
//            IndexRequest request = indexRequest(index, String.valueOf(BeanUtil.getKeyValue(document)), document);
//            request.source(convertAnalyzer(document.getClass()));
//
//            try {
//                clientPool.getClient().index(request, options);
//                PutMappingRequest mappingRequest = new PutMappingRequest(index);
//                mappingRequest.source(convertAnalyzer(document.getClass()));
//                client.indices().putMapping(mappingRequest, options);
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 插入索引数据
//     */
//    public void insertBatch(List list, Class clazz) throws Exception {
//        if (null != clientPool) {
//            String index = convertIndex(clazz);
//            RestHighLevelClient client = clientPool.getClient();
//
//            try {
//                BulkRequest bulkRequest = bulkRequest();
//                list.forEach(document -> {
//                    IndexRequest indexRequest = indexRequest(index, String.valueOf(BeanUtil.getKeyValue(document)), document);
//                    bulkRequest.add(indexRequest);
//                });
//                client.bulk(bulkRequest, options);
//
//                PutMappingRequest mappingRequest = new PutMappingRequest(index);
//                mappingRequest.source(convertAnalyzer(clazz));
//                client.indices().putMapping(mappingRequest, options);
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 更新索引数据
//     */
//    public void update(Object document) throws Exception {
//        if (null != clientPool) {
//            String index = convertIndex(document.getClass());
//            RestHighLevelClient client = clientPool.getClient();
//
//            try {
//                UpdateRequest request = updateRequest(index, String.valueOf(BeanUtil.getKeyValue(document)), document);
//                client.update(request, options);
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 更新索引数据
//     */
//    public void updateBatch(List list, Class clazz) throws Exception {
//        if (null != clientPool) {
//            String index = convertIndex(clazz);
//            RestHighLevelClient client = clientPool.getClient();
//
//            try {
//                BulkRequest bulkRequest = bulkRequest();
//                list.forEach(document -> {
//                    UpdateRequest request = updateRequest(index, String.valueOf(BeanUtil.getKeyValue(document)), document);
//                    bulkRequest.add(request);
//                });
//
//                client.bulk(bulkRequest, options);
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 删除索引数据
//     */
//    public void delete(String id, Class clazz) throws Exception {
//        if (null != clientPool) {
//            String index = convertIndex(clazz);
//            RestHighLevelClient client = clientPool.getClient();
//
//            try {
//                DeleteRequest request = deleteRequest(index, id);
//                client.delete(request, options);
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 删除索引数据
//     */
//    public void delete(Class clazz) throws Exception {
//        if (null != clientPool) {
//            String index = convertIndex(clazz);
//            RestHighLevelClient client = clientPool.getClient();
//
//            try {
//                DeleteIndexRequest request = deleteIndexRequest(index);
//                client.indices().delete(request, options);
//            } catch (IOException e) {
//                log.error("Elasticsearch 删除索引[{}]失败: {}", index, e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    /**
//     * 删除索引数据
//     */
//    public void delete(Object document) throws Exception {
//        if (null != clientPool) {
//            RestHighLevelClient client = clientPool.getClient();
//            String index = convertIndex(document.getClass());
//            String id = String.valueOf(BeanUtil.getKeyValue(document));
//
//            try {
//                if (StringUtil.isNotEmpty(id)) {
//                    DeleteRequest request = deleteRequest(index, id);
//                    client.delete(request, options);
//                } else {
//                    SearchResponse searchResponse = search(index, searchSourceBuilderWithEntity(document));
//                    Arrays.stream(searchResponse.getHits().getHits()).forEach(search -> {
//                        try {
//                            client.delete(deleteRequest(index, id), options);
//                        } catch (IOException e) {
//                            log.error("Elasticsearch 删除索引[{}]数据[{}]失败: {}", index, id, e.getMessage());
//                        }
//                    });
//                }
//            } catch (IOException e) {
//                log.error(e.getMessage());
//            } finally {
//                clientPool.returnClient(client);
//            }
//        }
//    }
//
//    private SearchSourceBuilder searchSourceBuilder() {
//        return new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
//    }
//
//    private SearchSourceBuilder searchSourceBuilderWithEntity(Object document) {
//        if (ObjectUtil.isEmpty(document)) {
//            return searchSourceBuilder();
//        }
//
//        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//        Arrays.stream(document.getClass().getDeclaredFields()).forEach(field -> {
//            String name = field.getName();
//            Object value = BeanUtil.getFieldValue(document, name);
//            if (null != value) {
//                searchSourceBuilder.query(QueryBuilders.termQuery(name, value));
//            }
//        });
//
//        return searchSourceBuilder;
//    }
//
//    private SearchSourceBuilder searchSourceBuilderWithParam(Map<String, Object> searchParams, Class clazz) {
//        if (MapUtil.isEmpty(searchParams)) {
//            return searchSourceBuilder();
//        }
//
//        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
//        BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
//        boolean isOr = false;
//        for (Map.Entry<String, Object> entry : searchParams.entrySet()) {
//            // 过滤掉空值
//            String name = entry.getKey();
//            Object value = entry.getValue();
//
//            if (!name.startsWith("Q_")) {
//                queryBuilder.must(QueryBuilders.termQuery(name, value));
//            } else {
//                String[] names = StringUtils.split(name, "_");
//                String filedName = names[1];
//                SqlOperator operator = SqlOperator.valueOf(names[3]);
//                if (SqlOperator.NULL != operator && SqlOperator.NNULL != operator
//                        && SqlOperator.EMP != operator && SqlOperator.NEMP != operator
//                        && null == value) {
//                    continue;
//                }
//
//                // 拆分operator、relation、filedAttribute
//                boolean hasRelation = (name.split("_").length == 5);
//                SqlRelation relation = SqlRelation.OR;
//                String symbol = null;
//                if (hasRelation) {
//                    symbol = names[4];
//                    relation = SqlRelation.valueOf(symbol.toUpperCase());
//                }
//
//                symbol = names[2];
//                value = Convert.invert(symbol, value);
//                String analyzer = null;
//                ExistsQueryBuilder existsQueryBuilder = null;
//                TermQueryBuilder termQueryBuilder = null;
//                MatchQueryBuilder matchQueryBuilder = null;
//                switch (operator) {
//                    case NNULL:
//                    case NEMP:
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.filter(QueryBuilders.wildcardQuery(filedName, "*"));
//                        } else {
//                            isOr = true;
//                            shouldQuery.should(QueryBuilders.wildcardQuery(filedName, "*"));
//                        }
//                        break;
//                    case EMP:
//                        termQueryBuilder = QueryBuilders.termQuery(filedName, "");
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.filter(termQueryBuilder);
//                        } else {
//                            isOr = true;
//                            shouldQuery.should(termQueryBuilder);
//                        }
//                    case NULL:
//                        existsQueryBuilder = QueryBuilders.existsQuery(filedName);
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.filter(existsQueryBuilder);
//                        } else {
//                            isOr = true;
//                            shouldQuery.should(existsQueryBuilder);
//                        }
//                        break;
//                    case EQ:
//                        termQueryBuilder = QueryBuilders.termQuery(filedName, value);
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.filter(termQueryBuilder);
//                        } else {
//                            isOr = true;
//                            shouldQuery.should(termQueryBuilder);
//                        }
//                        break;
//                    case NEQ:
//                        termQueryBuilder = QueryBuilders.termQuery(filedName, value);
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.mustNot(termQueryBuilder);
//                        } else {
//                            isOr = true;
//                            shouldQuery.mustNot(termQueryBuilder);
//                        }
//                        break;
//                    default:
//                        try {
//                            Field field = BeanUtil.getFieldByFieldName(clazz, filedName);
//                            com.piece.core.elasticsearch.annotations.Field source = field.getAnnotation(com.piece.core.elasticsearch.annotations.Field.class);
//                            if (null != source && source.searchAnalyzer()) {
//                                analyzer = source.analyzer().getType();
//                            }
//                        } catch (Exception e) {
//                        }
//
//                        matchQueryBuilder = QueryBuilders.matchQuery(filedName, value);
//                        if (StringUtil.isNotEmpty(analyzer)) {
//                            matchQueryBuilder.analyzer(analyzer);
//                        }
//
//                        if (relation.equals(SqlRelation.AND)) {
//                            queryBuilder.filter(matchQueryBuilder);
//                        } else {
//                            isOr = true;
//                            shouldQuery.should(matchQueryBuilder);
//                        }
//                }
//            }
//        }
//
//        if (isOr) {
//            queryBuilder.must(shouldQuery);
//        }
//
//        return searchSourceBuilder.query(queryBuilder);
//    }
//
//    private SearchSourceBuilder searchSourceBuilder(String queryStr) {
//        QueryBuilder queryBuilder;
//        if (StringUtil.isNotEmpty(queryStr)) {
//            queryBuilder = QueryBuilders.queryStringQuery(queryStr);
//        } else {
//            queryBuilder = QueryBuilders.matchAllQuery();
//        }
//        return new SearchSourceBuilder().query(queryBuilder);
//    }
//
//    private SearchSourceBuilder searchSourceBuilder(SearchSourceBuilder builder, String queryStr) {
//        QueryBuilder queryBuilder;
//        if (StringUtil.isNotEmpty(queryStr)) {
//            queryBuilder = QueryBuilders.queryStringQuery(queryStr);
//        } else {
//            queryBuilder = QueryBuilders.matchAllQuery();
//        }
//        return builder.query(queryBuilder);
//    }
//
//    private SearchRequest searchRequest(String index) {
//        return new SearchRequest(index);
//    }
//
//    private SearchRequest searchRequest(String index, String queryStr) {
//        return searchRequest(index, searchSourceBuilder(queryStr));
//    }
//
//    private SearchRequest searchRequest(String index, SearchSourceBuilder builder) {
//        SearchRequest searchRequest = new SearchRequest(index);
//        searchRequest.source(builder);
//        return searchRequest;
//    }
//
//    private SearchResponse search(String index) throws Exception {
//        SearchRequest searchRequest = searchRequest(index);
//        SearchResponse searchResponse = null;
//        RestHighLevelClient client = clientPool.getClient();
//
//        try {
//            searchResponse = client.search(searchRequest, options);
//        } catch (IOException e) {
//            log.error("Elasticsearch 搜索索引{}数据失败", index);
//        } finally {
//            clientPool.returnClient(client);
//        }
//
//        return searchResponse;
//    }
//
//    private SearchResponse search(String index, String queryStr) throws Exception {
//        return search(index, searchSourceBuilder(queryStr));
//    }
//
//    private SearchResponse search(String index, SearchSourceBuilder builder, String queryStr) throws Exception {
//        return search(index, searchSourceBuilder(builder, queryStr));
//    }
//
//    private SearchResponse search(String index, SearchSourceBuilder builder) throws Exception {
//        SearchRequest searchRequest = searchRequest(index, builder);
//        SearchResponse searchResponse = null;
//        RestHighLevelClient client = clientPool.getClient();
//
//        try {
//            searchResponse = client.search(searchRequest, options);
//        } catch (IOException e) {
//            log.error("Elasticsearch 搜索索引{}数据失败", index);
//        } finally {
//            clientPool.returnClient(client);
//        }
//
//        return searchResponse;
//    }
//
//    /**
//     * 分页查询
//     *
//     * @param queryTable  分页条件
//     * @param trackTotalHits 总数是否显示所有数据，默认只显示10000条
//     * @param isHighlighter 是否高亮标注
//     */
//    public DataTable searchPage(QueryTable queryTable, boolean trackTotalHits, boolean isHighlighter) {
//        Class clazz = queryTable.getEntity();
//        DataTable dataTable = new DataTable(queryTable);
//        dataTable.setData(Collections.emptyList());
//        dataTable.setRecordsTotal(0);
//
//        if (null != clientPool) {
//            try {
//                SearchSourceBuilder builder = searchSourceBuilderWithParam(queryTable.getParams(), clazz);
//                if (queryTable.getLength() != 0) {
//                    builder.from(queryTable.getStart()).size(queryTable.getLength());
//                    if (trackTotalHits) {
//                        builder.trackTotalHits(true);
//                    }
//                }
//
//                if (null != queryTable.getOrder()) {
//                    queryTable.getOrder().forEach(order -> {
//                        addSort(builder, order);
//                    });
//                }
//
//                setIsHighlight(builder, queryTable.getParams(), isHighlighter);
//                String index = convertIndex(clazz);
//                SearchResponse response = search(index, builder);
//                SearchHits searchHits = response.getHits();
//                long totalCnt = searchHits.getTotalHits().value;
//                List list = searchList(searchHits, clazz);
//                dataTable.setData(list);
//                dataTable.setRecordsTotal(Convert.toInt(totalCnt, 0));
//            } catch (Exception e) {
//                log.error("Elasticsearch 分页查询数据失败: {}", e.getMessage());
//            }
//        }
//
//        return dataTable;
//    }
//
//    public List searchList(String index, Map<String, Object> param, List<Order> orders, boolean isHighlighter, Class clazz) {
//        if (null == clientPool) {
//            try {
//                SearchSourceBuilder builder = searchSourceBuilderWithParam(param, clazz);
//                if (null != orders) {
//                    orders.forEach(order -> {
//                        addSort(builder, order);
//                    });
//                }
//
//                setIsHighlight(builder, param, isHighlighter);
//                SearchResponse response = search(index, builder);
//                return searchList(response.getHits(), clazz);
//            } catch (Exception e) {
//                log.error("Elasticsearch 查询数据失败: {}", e.getMessage());
//            }
//        }
//
//        return new ArrayList();
//    }
//
//    private List searchList(SearchHits searchHits, Class clazz) {
//        List list = new ArrayList<>();
//
//        if (null != searchHits) {
//            searchHits.forEach(item -> {
//                Object document = JsonUtil.toBean(item.getSourceAsString(), clazz);
//                Map<String, HighlightField> highlightFields = item.getHighlightFields();
//                if (null != highlightFields) {
//                    populateHighLightedFields(document, highlightFields);
//                }
//                list.add(document);
//            });
//        }
//
//        return list;
//    }
//
//    /**
//     * 组装高亮字符
//     *
//     * @param result 目标对象
//     * @param highlightFields 高亮配置
//     */
//    private <T> void populateHighLightedFields(T result, Map<String, HighlightField> highlightFields) {
//        for (HighlightField field : highlightFields.values()) {
//            try {
//                String name = field.getName();
//                if (!name.endsWith(".keyword")) {
//                    if (result instanceof ObjectNode) {
//                        ((ObjectNode)result).put(field.getName(), concat(field.fragments()));
//                    } else {
//                        BeanUtil.setFieldValue(result, field.getName(), concat(field.fragments()));
//                    }
//                }
//            } catch (Exception e) {
//                log.error("failed to set highlighted value for field: [{}] with value: [{}]", field.getName(), Arrays.toString(field.getFragments()), e);
//            }
//        }
//    }
//
//    /**
//     * 拼凑数组为字符串
//     */
//    private String concat(Text[] texts) {
//        StringBuffer sb = new StringBuffer();
//        for (Text text : texts) {
//            sb.append(text.toString());
//        }
//        return sb.toString();
//    }
//
//    private void addSort(SearchSourceBuilder searchBuilder, Order order) {
//        if (null != order && StringUtil.isNotEmpty(order.getAttr())) {
//            switch (order.getSort()) {
//                case ASC:
//                    searchBuilder.sort(order.getAttr(), SortOrder.ASC);
//                    break;
//                case DESC:
//                default:
//                    searchBuilder.sort(order.getAttr(), SortOrder.DESC);
//            }
//        }
//    }
//
//    /**
//     * 设置是否需要高亮处理
//     */
//    private void setIsHighlight(SearchSourceBuilder searchSourceBuilder, Map<String, Object> params, boolean isHighlighter) {
//        if (isHighlighter) {
//            HighlightBuilder highlightBuilder = new HighlightBuilder();
//            for (Map.Entry<String, Object> entry : params.entrySet()) {
//                String name = entry.getKey();
//                if (!name.startsWith("Q_")) {
//                    highlightBuilder.field(name).preTags(HIGHLIGHTER_PRE_TAGS).postTags(HIGHLIGHTER_POST_TAGS);
//                } else {
//                    highlightBuilder.field(StringUtils.split(name, "_")[1]).preTags(HIGHLIGHTER_PRE_TAGS).postTags(HIGHLIGHTER_POST_TAGS);
//                }
//            }
//
//            searchSourceBuilder.highlighter(highlightBuilder);
//        }
//    }
//
//    private String convertIndex(Class clazz) {
//        Document document = (Document) clazz.getAnnotation(Document.class);
//        if (null == document) {
//            return clazz.getSimpleName().toLowerCase();
//        }
//
//        return document.indexName();
//    }
//
//    private Map<String, Object> convertToMap(Object document) {
//        Map<String, Object> result = new HashMap<>();
//        Field[] fields = document.getClass().getDeclaredFields();
//        Arrays.stream(fields).forEach(field -> {
//            try {
//                String name = null;
//                field.setAccessible(true);
//                Object value = field.get(document);
//                com.piece.core.elasticsearch.annotations.Field source = field.getAnnotation(com.piece.core.elasticsearch.annotations.Field.class);
//                if (null != source) {
//                    name = StringUtil.isNotEmpty(source.name()) ? source.name() : field.getName();
//                    result.put(name, value);
//                }
//            } catch (IllegalAccessException e) {
//            }
//        });
//
//        return result;
//    }
//
//    private XContentBuilder convertAnalyzer(Class clazz) {
//        XContentBuilder builder = null;
//        Field[] fields = clazz.getDeclaredFields();
//        try {
//            builder = XContentFactory.jsonBuilder();
//            builder.startObject();
//            builder.startObject("properties");
//            for (int i = 0; i < fields.length; i++) {
//                Field field = fields[i];
//                String name = null;
//                com.piece.core.elasticsearch.annotations.Field source = field.getAnnotation(com.piece.core.elasticsearch.annotations.Field.class);
//                if (null != source && source.searchAnalyzer()) {
//                    name = StringUtil.isNotEmpty(source.name()) ? source.name() : field.getName();
//                    builder.startObject(name);
//                    builder.field("type", source.type().getType());
//                    builder.field("analyzer", source.analyzer().getType());
//                    builder.endObject();
//                }
//            }
//            builder.endObject();
//            builder.endObject();
//        } catch (Exception e) {
//        }
//
//        return builder;
//    }
//}
